// ==UserScript==
// @name Block Specified Websites with Control Panel (Vue & Element UI)
// @namespace https://bbs.tampermonkey.net.cn/
// @version 1.7
// @description 使用 Vue 与 Element UI 重构的屏蔽网站脚本,提供可拖动的控制面板来封禁/解除封禁当前网站,并管理封禁列表,同时支持隐藏后以悬浮小球形式显示
// @author
// @match *://*/*
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
/* =======================
工具函数:加载外部资源
========================= */
function loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
function loadCSS(url) {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = "stylesheet";
link.href = url;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
}
/* =======================
数据存储与基本判断
========================= */
let blockedSites = GM_getValue('blockedSites', null);
if (!blockedSites) {
blockedSites = ["youtube.com"];
GM_setValue('blockedSites', blockedSites);
}
const currentHost = window.location.hostname;
function isSiteBlocked(host) {
return blockedSites.some(site => host === site || host.endsWith('.' + site));
}
/* =======================
屏蔽页面(覆盖整个 body)
========================= */
function blockPage() {
const body = document.body;
if (!body) return;
body.innerHTML = '';
body.style.margin = '0';
const blockDiv = document.createElement('div');
blockDiv.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #ff4444;
color: white;
font-size: 3em;
display: flex;
justify-content: center;
align-items: center;
z-index: 99998;
cursor: not-allowed;
`;
blockDiv.innerHTML = '
⚠️ 禁止访问此网站 ⚠️
';
body.appendChild(blockDiv);
}
if (isSiteBlocked(currentHost)) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', blockPage);
} else {
blockPage();
}
// 定时不断重置页面位置和覆盖层
setInterval(() => {
window.scrollTo(0, 0);
blockPage();
}, 500);
}
/* =======================
为 Vue 应用创建容器
========================= */
function createContainer() {
const container = document.createElement('div');
container.id = 'block-control-app';
// 将容器添加到 元素中,以免被 body 覆盖
document.documentElement.appendChild(container);
}
/* =======================
加载 Vue 与 Element UI 资源
========================= */
function loadLibraries() {
return Promise.all([
loadCSS('https://unpkg.com/element-ui/lib/theme-chalk/index.css'),
loadScript('https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js'),
loadScript('https://unpkg.com/element-ui/lib/index.js')
]);
}
/* =======================
自定义拖拽指令
用法说明:
- 对于需要拖拽的元素,如果希望拖动时移动的是其父元素,
则在指令中传参: v-drag:parent
- 否则直接 v-drag 使该元素自身可拖动
========================= */
function registerDragDirective(Vue) {
Vue.directive('drag', {
bind(el, binding) {
const dragTarget = binding.arg === 'parent' ? el.parentElement : el;
el.style.cursor = 'move';
el.onmousedown = function(e) {
e.preventDefault();
const disX = e.clientX - dragTarget.offsetLeft;
const disY = e.clientY - dragTarget.offsetTop;
function onMouseMove(e) {
dragTarget.style.left = (e.clientX - disX) + 'px';
dragTarget.style.top = (e.clientY - disY) + 'px';
}
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
};
}
});
}
/* =======================
初始化 Vue 应用
控制面板采用 Element UI 的卡片、按钮、输入框与折叠面板构造,
同时根据 visible 状态显示或隐藏面板;隐藏时显示一个可拖拽的悬浮小球。
========================= */
function initVueApp() {
new Vue({
el: '#block-control-app',
data: {
blockedSites: blockedSites,
newSite: '',
currentHost: currentHost,
visible: true // 控制面板是否显示
},
computed: {
isBlocked() {
return this.blockedSites.some(site => this.currentHost === site || this.currentHost.endsWith('.' + site));
}
},
methods: {
toggleCurrentSite() {
if (this.isBlocked) {
this.blockedSites = this.blockedSites.filter(site => !(this.currentHost === site || this.currentHost.endsWith('.' + site)));
GM_setValue('blockedSites', this.blockedSites);
alert("已解除封禁此网站,请刷新页面。");
location.reload();
} else {
this.blockedSites.push(this.currentHost);
GM_setValue('blockedSites', this.blockedSites);
alert("已封禁此网站,请刷新页面。");
location.reload();
}
},
addSite() {
const newSiteTrim = this.newSite.trim();
if (newSiteTrim && !this.blockedSites.includes(newSiteTrim)) {
this.blockedSites.push(newSiteTrim);
GM_setValue('blockedSites', this.blockedSites);
this.newSite = '';
}
},
removeSite(index) {
this.blockedSites.splice(index, 1);
GM_setValue('blockedSites', this.blockedSites);
location.reload();
},
hidePanel() {
this.visible = false;
},
showPanel() {
this.visible = true;
}
},
template: `
网站屏蔽面板
隐藏
{{ isBlocked ? '解除封禁此网站' : '封禁此网站' }}
添加
显示
`
});
}
/* =======================
主初始化流程
========================= */
function init() {
createContainer();
loadLibraries().then(() => {
registerDragDirective(Vue);
initVueApp();
}).catch(err => {
console.error('加载 Vue 或 Element UI 失败:', err);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();